Tomcat 的配置相关~
Tomcat 是什么
参考资料 理解Tomcat工作原理 参考资料 民间 Tomcat8 指南
学习了这么久的 javaWeb 知识还不知道 Tomcat 是怎么工作的,只知道它是一个 Servlet 容器,实属惭愧
按功能分类,Web Server可以分为
|- Web Server
|- Http Server
|- Application Server
|- Servlet Container
|- CGI Server
|- ......
注:CGI:Common Gateway Interface 公共网关接口
Http Server:主要用来做静态内容服务、代理服务器、负载均衡等。直面外来请求转发给后面的应用服务(Tomcat 之类的)
Application Server 往往是运行在 HTTP Server 的背后,执行应用,将动态的内容转化为静态的内容之后,通过 HTTP Server 分发到客户端
请求响应对象原理
1、Tomcat 服务器会根据请求 url 中的资源路径创建对应的 Servlet 的对象
2、Tomcat 服务器会创建 Request 和 Response 对象,并在 Request 对象中封装请求消息数据
3、Tomcat 再把创建的 Request 和 Response 对象传递给 Service 方法(doGet 或 doPost)
4、后台通过 Request 取得请求消息数据,再通过 Response 对象设置响应体消息数据,最后把这个响应返回回去
安装 Tomcat
wget https://mirror-hk.koddos.net/apache/tomcat/tomcat-10/v10.0.5/bin/apache-tomcat-10.0.5.tar.gz
tar -zxvf apache-tomcat-10.0.5.tar.gz
配置环境变量,添加 TOMCAT_HOME、TOMCAT_PORT 环境变量
vim ~/.bashrc
# 创建 TOMCAT_HOME 环境变量, 指向 Tomcat 根目录
export TOMCAT_HOME=/home/alsritter/JavaTool/apache-tomcat-10.0.5
# 创建 Tomcat 服务器的端口号环境变量
export TOMCAT_PORT=8080
# PS: 以后 Tomcat 升级版本或目录变更, 默认端口变更, 修改这里即可
# 刷新
source ~/.bashrc
# 检查是否添加成功
echo $TOMCAT_PORT
echo $TOMCAT_HOME
然后每次启动 Tomcat 都需要进到 Tomcat 目录下面执行 startup.bat
有点麻烦,所以,这里可以编写一个脚本来直接启动
创建启动脚本命令
在 /usr/local/bin
目录下创建一个空的命令文件,命名为 tomcat
sudo touch /usr/local/bin/tomcat
PS: 由于 /usr/local/bin
目录默认就在 PATH 环境变量中,因此存放在 /usr/local/bin
目录下的命令在任何路径下都可以直接执行。
给命令文件添加可执行权限
sudo chmod +x /usr/local/bin/tomcat
编辑命令文件
sudo vim /usr/local/bin/tomcat
添加以下脚本内容到 tomcat
命令到文件中:
#!/bin/sh
DIR="$(cd "$(dirname "$0")" && pwd)"
cd $DIR
# 先判断需要用到的环境变量是否存在
if [ "${TOMCAT_HOME}" = "" ] || [ "${TOMCAT_PORT}" = "" ] ; then
# 需要先配置 TOMCAT_HOME 和 TOMCAT_PORT 环境变量
# export TOMCAT_HOME=Tomcat安装根目录
# export TOMCAT_PORT=Tomcat端口
echo "TOMCAT_HOME or TOMCAT_PORT environment variable not exists!"
echo
else
# 获取命令的第2个参数(第一个参数 $0 是命令自己本身)
OPERATOR="$1"
# 进入到 Tomcat 的命令目录
cd "${TOMCAT_HOME}/bin"
#
# 下面开始根据不同的参数执行相应的命令
#
if [ "${OPERATOR}" = "start" ] ; then
# 执行启动命令
./startup.sh
elif [ "${OPERATOR}" = "restart" ] ; then
# 执行重启命令(先停止, 再启动)
./shutdown.sh
./startup.sh
elif [ "${OPERATOR}" = "stop" ] ; then
# 执行停止命令
./shutdown.sh
elif [ "${OPERATOR}" = "status" ] ; then
# 输出监听状态, 主要是看是否有在监听 Tomcat 端口
netstat -npl | grep ":${TOMCAT_PORT}"
else
# 没有符合的参数, 输出命令格式说明
echo "Usage: tomcat <start|restart|stop|status>"
echo
fi
fi
保存成功后,在终端任何路径下执行命令管理 Tomcat:
#
# tomcat 命令中需要使用到管理员权限, 先切换到管理员账户
#
su
tomcat start # 启动 Tomcat
tomcat restart # 重启 Tomcat
tomcat stop # 停止 Tomcat
tomcat status # 查看 Tomcat 状态
修改默认端口
在Tomcat的根(安装)目录下,有一个 conf
文件夹,双击进入 conf
文件夹,在里面找到 Server.xml
文件,打开该文件。
其次:在文件中找到如下文本:
<Connector port="8080" protocol="HTTP/1.1"
maxThreads="150" connectionTimeout="20000"
redirectPort="8443" />
<!-- 也有可能是这样的: -->
<Connector port="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000"
disableUploadTimeout="true" />
改成对应端口就行了
添加 API 包
Servlet API 是一个 jar 包,我们需要通过 Maven 来引入它,才能正常编译。
编写 pom.xml
文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>web-servlet-hello</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>hello</finalName>
</build>
</project>
注意到这个 pom.xml
与普通 Java程序有个区别,打包类型不是 jar,而是 war,表示 Java Web Application Archive:
<packaging>war</packaging>
引入的 Servlet API 如下:
注意到 <scope>
指定为 provided,表示编译时使用,但不会打包到 .war
文件中,因为运行期 Web 服务器本身已经提供了 Servlet API 相关的 jar 包。
我们还需要在工程目录下创建一个 web.xml
描述文件,放到 src/main/webapp/WEB-INF
目录下(固定目录结构,不要修改路径,注意大小写)。
文件内容可以固定如下:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
Servlet 的使用
// 只要在Servlet上设置@WebServlet标注,容器就会自动读取当中的信息
@WebServlet("/")
public class TempServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp){
System.out.println("收到请求");
}
}
IDEA 里面创建对象时可以直接创建 Servlet
注:使用 Chrome 请求一次执行两次 doGet 的原因:
Chrome 会发送除了请求的那个外,还会执行 /localhost/favicon.ico
这个 url ,上面的那个使用例子的路由是 /
根路径,所以会导致执行两次请求
使用嵌入式 Tomcat 开发
参考资料 Create a Java Web Application Using Embedded Tomcat
上面那种手动丢到 Tomcat的 webapp 目录的方式实在太麻烦了,而且它调试还需要打开 Tomcat 的远程调试端口并且连接上去。
- 启动 JVM并执行 Tomcat 的
main()
方法; - 加载 war 并初始化 Servlet;
- 正常服务。
启动 Tomcat 无非就是设置好 classpath 并执行 Tomcat 某个 jar 包的 main()
方法,我们完全可以把 Tomcat 的 jar 包全部引入进来,然后自己编写一个 main()
方法,先启动 Tomcat,然后让它加载我们的 webapp 就行。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itranswarp.learnjava</groupId>
<artifactId>web-servlet-embedded</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<java.version>11</java.version>
<tomcat.version>9.0.26</tomcat.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
<!-- 如果要使用 JSP 则需要引入下面两个依赖 -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
</project>
引入依赖 tomcat-embed-core 和 tomcat-embed-jasper,引入的 Tomcat 版本 <tomcat.version>
为9.0.26。
不必引入 Servlet API,因为引入 Tomcat 依赖后自动引入了 Servlet API。因此,我们可以正常编写 Servlet 如下:
@WebServlet(urlPatterns = "/")
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
String name = req.getParameter("name");
if (name == null) {
name = "world";
}
PrintWriter pw = resp.getWriter();
pw.write("<h1>Hello, " + name + "!</h1>");
pw.flush();
}
}
然后,我们编写一个 main()
方法,启动Tomcat服务器:
public class Main {
public static void main(String[] args) throws Exception {
// 启动 Tomcat:
Tomcat tomcat = new Tomcat();
tomcat.setPort(Integer.getInteger("port", 8080));
tomcat.getConnector();
/**
* tomcat.start();
* 到上面这一步其实已经可以把 tomcat 起动了,只是现在启动里面没啥东西
*/
// 创建 webapp:
// 把class加载进来,把启动的工程加入进来了
Context ctx = tomcat.addWebapp("", new File("src/main/webapp").getAbsolutePath());
WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(
new DirResourceSet(resources, "/WEB-INF/classes",
new File("target/classes").getAbsolutePath(), "/")
);
ctx.setResources(resources);
tomcat.start();
tomcat.getServer().await();
}
}
这样,我们直接运行 main()
方法,即可启动嵌入式 Tomcat 服务器,然后,通过预设的 tomcat.addWebapp("", new File("src/main/webapp")
,Tomcat 会自动加载当前工程作为根 webapp,可直接在浏览器访问 http://localhost:8080/
启动特别慢的问题
Tomcat启动因为 Creation of SecureRandom instance for session ID generation using [SHA1PRNG] 卡住
这是 JVM 的 BUG
需要在 JVM 启动参数加上:
-Djava.security.egd=file:/dev/./urandom
编码问题
在启动项的 VM 设置里加上 -Dfile.encoding=UTF-8
热部署 Tomcat
每次 IDEA 启动 Tomcat 实际上都是实例化一个新的进程,而用完就释放;这样做效率太低了,所以可以使用热部署来加快开发效率
Run
--> Edit Configuration
设置为 On frame deactivation IDE
会失去焦点的情况下自动更新,每次失去焦点就自动触发 update
没必要,设置为 Do nothing
手动更新
Debug 和 Run 的更新策略是不同的
- 运行模式下(jsp 立即生效,java 需要 redeploy 才可生效)
- 调试模式下(java、jsp 都立即生效)
war 和 war exploded 区别
war
模式:将 WEB
工程以包的形式上传到服务器;这种可以称之为是发布模式,先打成 war
包,再发布;所以能在 Tomcat 的 webapps
文件夹下看到发布的项目
war exploded
模式:将 WEB 工程以当前文件夹的位置关系上传到服务器;即直接把文件夹、jsp页面 、classes 等等移到 Tomcat 部署文件夹里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式。(所以并没实际打包 war,因此在 wabapps
文件夹里找不到这个文件夹)